/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       text_window.cpp

	$Header: /game/text_window.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "text_window.h"

#include "abstract_bitmap.h"
#include "bitmap_group_cache.h"
#include "bitmap_group_ptr.h"
#include "bitmap_group_window.h"
#include "bound_handler.h"
#include "button.h"
#include "convert_16_bit.h"
#include "font.h"
#include "paint_surface.h"
#include "scrollbar.h"
#include "string_vector.h"

t_button* create_blacksmith_button( std::string const& name, t_bitmap_group_ptr bitmap_ptr, t_window *parent_window );

extern t_bitmap_group_cache const    k_arrow_button_bitmaps ("control.button_scroll");

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
t_text_window::t_text_window( t_cached_font& font, t_screen_rect const& rect, t_window* parent,
							  std::string const& text, t_pixel_24 const& color, bool disable_scrollbar, bool start_at_bottom )
             : t_window( k_some_transparency, parent), m_font( font ), m_text( text ), m_scrollbar_active( false ),
			   m_disable_scrollbar( disable_scrollbar ), m_start_at_bottom( start_at_bottom )
{
	m_scroll_pixel_position = 0;
	m_color = convert_to_16_bit( color );
	m_shadow_color = 0;
	m_justification = k_justify_left;
	m_center_vertical = false;
	m_drop_shadow = false;
	m_button_up = 0;
    m_button_down = 0;
	m_button_scroll_active = false;

	// Initialize our base class
	init( rect );

	std::string newtext;
	if( m_start_at_bottom )
	{
		// pad the begining of the text with number of lines window can contain
		int num_lines = rect.height() / m_font->get_height();
		for( int i = 0; i < num_lines; ++i )
			newtext += "\n";
		newtext += m_text;
		m_text = newtext;
	} else
	newtext = m_text;
	
	m_text_width = m_font->wrap_text( m_text_block, newtext.c_str(), rect.width() );
	init_scrollbar();
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
t_text_window::t_text_window( t_cached_font& font, t_screen_point point, t_window* parent, 
							  std::string const& text, t_pixel_24 const& color)
             : t_window( k_some_transparency, parent ), m_font( font ), m_text( text ), m_scrollbar_active( false ), 
				m_start_at_bottom( false )
{
	m_scroll_pixel_position = 0;
	m_color = convert_to_16_bit( color );
	m_shadow_color = 0;
	m_justification = k_justify_left;
	m_center_vertical = false;
	m_drop_shadow = false;
	m_button_up = 0;
    m_button_down = 0;
	m_button_scroll_active = false;
	init( font->get_rect( text.c_str() ) + point );
	m_text_width = m_font->wrap_text( m_text_block, m_text.c_str(), get_screen_rect().width() );
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
t_text_window::t_text_window( t_cached_font& font, t_window* parent, char const* text, 
							  t_pixel_24 color )
			 : t_window( k_some_transparency, parent ), m_font( font ), m_text( text ), m_scrollbar_active( false ),
				m_start_at_bottom( false )
{
	m_scroll_pixel_position = 0;
	m_color = convert_to_16_bit( color );
	m_justification = k_justify_left;
	m_center_vertical = false;
	m_button_up = 0;
    m_button_down = 0;
	m_button_scroll_active = false;

	m_bfirst		= true;
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
void t_text_window::init_scrollbar()
{
	t_screen_rect rect = get_client_rect();
	int			  block_height = m_font->get_height() * m_text_block.size();


	m_scroll_max_value = 0;

	// check text against window size to see if we
	// are going to need a vertical scrollbar, and if so, make one
	if( rect.height() < block_height && rect.height() >= 100 && rect.width() >= 40 )
	{
		if ( !m_disable_scrollbar )
		{
			if (m_scrollbar == 0)
			{
				m_scrollbar = new t_scrollbar( rect.top_left(), rect.height(), this, 0, 0 );
				m_scrollbar->set_handler( bound_handler( *this, &t_text_window::slider_change ));
			}
			else
			{
				m_scrollbar->set_visible( true );
			}

			t_screen_rect new_rect = rect;
			
			new_rect.left = rect.width() - m_scrollbar->get_width();
			m_scrollbar->move( new_rect );
			m_scrollbar_active = true;
			m_scrollbar->set_position(0);

			// re-wrap the text

			new_rect = rect;
			new_rect.right = new_rect.right - m_scrollbar->get_width() - 8;
			m_text_width = m_font->wrap_text( m_text_block, m_text.c_str(), new_rect.width() );
		}
		else
		{
			m_text_width = m_font->wrap_text( m_text_block, m_text.c_str(), rect.width() );
		}

		int text_height = m_font->get_wrapped_height( m_text.c_str(), m_text_width );

		m_scroll_pixel_position = 0;

		if( !m_disable_scrollbar )
		{
			int rect_height = rect.height() / m_font->get_height();
			text_height /= m_font->get_height();
			m_scroll_max_value = text_height - rect_height;
			m_scrollbar->set_max_value( m_scroll_max_value );
			m_scroll_step = m_font->get_wrapped_height( m_text.c_str(), m_text_width ) / m_scroll_max_value;
			m_scrollbar->set_page_size( rect_height );
		} else
		{
			// these settings use no scrollbar and expect the client to handle any scrolling on it's own
			// passes back scroll values by pixel, not line if client prefers to smooth scroll
			m_scroll_max_value = text_height - rect.height();
			m_scroll_step = 1;
			m_scrollbar_active = false;
		}
	}
    else if( rect.height() < block_height && rect.height() >= 60 )
	{
		if (m_scrollbar != 0)
			m_scrollbar->set_visible( false );
		m_scrollbar_active = false;
		
        if( !m_disable_scrollbar )
		{
			if ((m_button_up == 0) && (m_button_down == 0))
			{
                m_button_up = create_blacksmith_button( "up", k_arrow_button_bitmaps.get(), this );
                m_button_down = create_blacksmith_button( "down", k_arrow_button_bitmaps.get(), this );
                m_button_up->set_click_handler( bound_handler( *this, &t_text_window::button_scroll_up_arrow ));
                m_button_down->set_click_handler( bound_handler( *this, &t_text_window::button_scroll_down_arrow ));
			}
			else
			{
				m_button_up->set_visible( true );
				m_button_down->set_visible( true );
			}
			t_screen_rect   new_rect = rect;
			new_rect.left = rect.width() - m_button_up->get_width();
            m_button_up->move( new_rect.top_left() );
			new_rect.top = rect.height() - m_button_down->get_height();
            m_button_down->move( new_rect.top_left() );
	        m_button_scroll_active = true;
			
			new_rect = rect;
			new_rect.right = new_rect.right - m_button_up->get_width();
            m_text_width = m_font->wrap_text( m_text_block, m_text.c_str(), new_rect.width() );
        }
		int text_height = m_font->get_wrapped_height( m_text.c_str(), m_text_width );

		m_scroll_pixel_position = 0;

		if( !m_disable_scrollbar )
		{
			int rect_height = rect.height() / m_font->get_height();
			text_height /= m_font->get_height();
			m_scroll_max_value = text_height - rect_height;
			m_scroll_step = m_font->get_wrapped_height( m_text.c_str(), m_text_width ) / m_scroll_max_value;
		} else
		{
			// these settings use no scrollbar and expect the client to handle any scrolling on it's own
			// passes back scroll values by pixel, not line if client prefers to smooth scroll
			m_scroll_max_value = text_height - rect.height();
			m_scroll_step = 1;
		}

	}
	else
	{
		if (m_scrollbar != 0)
			m_scrollbar->set_visible( false );
		m_scrollbar_active = false;
		if (m_button_up != 0)
			m_button_up->set_visible( false );
        if (m_button_down != 0)
			m_button_down->set_visible( false );
        m_button_scroll_active = false;
    }
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
void t_text_window::slider_change( t_scrollbar* scrollbar, int value )
{
//	m_scroll_pixel_position = scrollbar->get_position() * m_scroll_step;
	m_scroll_pixel_position = scrollbar->get_position();
	invalidate();
}

void t_text_window::button_scroll_up_arrow( t_button * )
{
	if (m_scroll_pixel_position > 0)
	    m_scroll_pixel_position -= 1; //m_scroll_step;
	invalidate();
}

void t_text_window::button_scroll_down_arrow( t_button * )
{
	if (m_scroll_pixel_position < m_scroll_max_value )
	    m_scroll_pixel_position += 1; //m_scroll_step;
	invalidate();
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
void t_text_window::on_text_change()
{
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
void t_text_window::set_font( t_cached_font& font )
{
	m_font = font;
	m_text_width = m_font->wrap_text( m_text_block, m_text.c_str(), get_screen_rect().width() );
	invalidate();
	init_scrollbar();
	on_text_change();
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
void t_text_window::set_text( std::string const& text )
{
	m_bfirst	= true;
	m_text = text;
	m_text_width = m_font->wrap_text( m_text_block, m_text.c_str(), get_screen_rect().width() );
	invalidate();
	init_scrollbar();
	on_text_change();
}

// --------------------------------------------------------------
// Set text, with wrapping already done. It is the caller's 
// responsibility to make sure text will fit in window! If scrollbars
// are not disabled, text may be rewrapped. 
// --------------------------------------------------------------
void t_text_window::set_wrapped_text( t_string_vector const & wrapped_text, int text_width )
{
	m_text_block = wrapped_text;
	m_text_width = text_width;

	m_text.erase();

	t_string_vector::const_iterator it;
	for ( it = wrapped_text.begin(); it != wrapped_text.end(); it++ )
	{
		m_text += *it;
		m_text += "\n";
	}

	invalidate();
	init_scrollbar();
	on_text_change();
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
void t_text_window::set_color( t_pixel_24 color )
{
	m_color = convert_to_16_bit( color );
	invalidate();
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
void t_text_window::set_drop_shadow( bool arg, t_pixel_24 const& shadow_color )
{
	m_drop_shadow = arg;
	m_shadow_color = convert_to_16_bit( shadow_color );
	invalidate();
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
t_screen_point t_text_window::get_row_start( int row ) const
{
	t_screen_rect  client_rect = get_client_rect();
	t_screen_point result;

	result.x = 0;
	result.y = row * m_font->get_height();
	if (m_center_vertical)
	{
		int height = m_text_block.size() * m_font->get_height();

		if (height < client_rect.height())
			result.y += (client_rect.height() - height) / 2;
	}

	int width = 0;
	if( m_text_block.size() != 0 )
		width = m_font->get_width( m_text_block[row].c_str() );

	switch (m_justification)
	{

		case k_justify_center:
			result.x = (client_rect.width() - width ) / 2;
			break;
		case k_justify_right:
			result.x = client_rect.width() - width;
			break;
	}
	return result;
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
void t_text_window::paint( t_paint_surface& paint_surface )
{
	t_abstract_bitmap16_ptr	dest = paint_surface.get_bitmap();
	t_screen_point          origin = -paint_surface.get_rect().top_left();
	t_screen_point          dest_point = origin;
	t_screen_point			dest_offs;
	t_screen_rect           client_rect = get_client_rect();
	int                     height;
	int                     i;
	bool					usebuffer;
	int						count	= client_rect.height()/m_font->get_height();
	if(count>=15)
		usebuffer	= true;
	else 
		usebuffer	= false;

	height = m_text_block.size() * m_font->get_height();

	if (m_center_vertical && height < client_rect.height())
		dest_point.y += (client_rect.height() - height) / 2;

	int start_line = m_scroll_pixel_position;
	int start_pixel = 0;
    int text_window_width;

	if( m_scrollbar_active )
	{
		start_line = m_scroll_pixel_position;
        // Account for scrollbar.
        text_window_width = client_rect.width() - m_scrollbar->get_width();
	} else if( m_button_scroll_active )
	{
		start_line = m_scroll_pixel_position;
        // Account for scrollbar.
        text_window_width = client_rect.width() - m_button_up->get_width();
	} else
	{
		start_line = m_scroll_pixel_position / m_font->get_height();
		start_pixel = m_scroll_pixel_position % m_font->get_height();
        // Account for scrollbar.
        text_window_width = client_rect.width();
	}

//	if(!m_bfirst)
//	{
//		return;
//	}
//	if(m_text_block.size()>start_line+30)
//		m_bfirst	= false;



	for (i = start_line; i < m_text_block.size(); i++)
	{
		// NOTE:  If you want to do this, you'll need a bool control variable to turn
		// it on/off so that auto-scrolling text (as for campaign intro) stay smooth.
#if 0
		// Don't draw partial lines
		if (dest_point.y + m_font->get_height() > client_rect.bottom)
			return;
#endif

		switch (m_justification)
		{
			case k_justify_center:
				dest_point.x = origin.x + (text_window_width
		                                   - m_font->get_width( m_text_block[i].c_str())) / 2;
				break;
			case k_justify_right:
				dest_point.x = origin.x + text_window_width
										  - m_font->get_width( m_text_block[i].c_str());
				break;
		}

		if (dest_point.x < origin.x)
			dest_point.x = origin.x;

		dest_offs = dest_point;
		if( !m_scrollbar_active && !m_button_scroll_active )
			dest_offs.y -= start_pixel;

		m_font->draw_to( *dest, dest_offs, m_text_block[i].c_str(), m_color, m_drop_shadow, m_shadow_color ,(usebuffer) ? (UINT)this : NULL,i);

		dest_point.y += m_font->get_height();

		if(i-start_line>count)
			break;
	}
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
void t_text_window::on_size_change( t_screen_rect const& rect )
{
	m_text_width = m_font->wrap_text( m_text_block, m_text.c_str(), rect.width() );
	init_scrollbar();
	on_text_change();
}

// --------------------------------------------------------------
// text window class
// --------------------------------------------------------------
int t_text_window::get_text_height() const
{
	return m_text_block.size() * m_font->get_height();
}



// --------------------------------------------------------------
// --------------------------------------------------------------
// --------------------------------------------------------------
// --------------------------------------------------------------



// --------------------------------------------------------------
// scrolling text window class
// --------------------------------------------------------------
t_scrolling_text_window::t_scrolling_text_window( t_cached_font& font, t_screen_rect const& rect, t_window* parent,
							  std::string const& text, t_pixel_24 const& color, int total_time )
             : t_text_window( font, rect, parent, text, color, true, true ), t_idle_processor( 0 ),
				m_delay( 50 ), m_total_time( total_time )
{
	m_end_handler = 0;
	m_elapsed_time = 0;
	m_pixel_time_elapsed = 0;
	m_delay_per_pixel =  m_total_time / m_scroll_max_value;
	if( m_delay_per_pixel > 50 )
		m_delay = m_delay_per_pixel;
	else
		m_delay = 50;
	set_delay( m_delay );
}

// --------------------------------------------------------------
// scrolling text window class
// --------------------------------------------------------------
void t_scrolling_text_window::on_idle()
{

	if ( m_scroll_pixel_position <= m_scroll_max_value )
	{

		if ( ( m_pixel_time_elapsed += m_delay ) >= m_delay_per_pixel )
		{
			m_pixel_time_elapsed = 0;
			m_scroll_pixel_position++;
			invalidate();
		}
	} else
	{
		t_window_ptr ref = this;

		suspend_idle_processing(); 
		m_end_handler( this ); // no check for null required, that's built into t_handlers.
		m_end_handler = t_window_handler(); 
	}
}

